//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <ddk.h>
#include <xllp_dmac.h>
#include <bulverde.h>
#include "ccamera.h"

#if (_TEST_TYPE == 41)      /* Test for Runtime of ISR */
    #define SAMPLETIMES 50
    ULONG uTimerStart, uTimerEnd;
	ULONG ulCapturePhotoTime[SAMPLETIMES];
	INT nCapturePhotoSampleCounter = 0;
#endif //_TEST_TYPE == 41

class CCamera : public Driver {
    public:
    CARAPI Read(
        /* [in] */ UINT64 u64Offset,
        /* [in] */ UINT uNumberOfBytesToRead,
        /* [out] */ EzByteBuf ebbData,
        /* [out] */ IEvent * * ppCompletionEvent);
    CARAPI Write(
        /* [in] */ UINT64 u64Offset,
        /* [in] */ EzByteBuf ebbData,
        /* [out] */ UINT * puNumberOfBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent);
    CARAPI Control(
        /* [in] */ INT nControlCode,
        /* [in] */ EzByteBuf ebbInData,
        /* [out] */ EzByteBuf * pOutData,
        /* [out] */ IEvent * * ppCompletionEvent);

    virtual void Dispose();
    BOOL   Install();

public:
     CamDrv_Context CurrentContext;
};


ECode CCamera::Read(
        /* [in] */ UINT64 u64Offset,
        /* [in] */ UINT uNumberOfBytesToRead,
        /* [out] */ EzByteBuf ebbData,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode CCamera::Write(
        /* [in] */ UINT64 u64Offset,
        /* [in] */ EzByteBuf ebbData,
        /* [out] */ UINT * puNumberOfBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}


void PoSetCameraPowerOn(BOOL PowerOn);
void PoSetLcdPowerOn(BOOL PowerOn);
ECode CCamera::Control(
        /* [in] */ INT nControlCode,
        /* [in] */ EzByteBuf ebbInData,
        /* [out] */ EzByteBuf * pOutData,
        /* [out] */ IEvent * * ppCompletionEvent)
{

    ECode ec = NOERROR;
    Camera_Format *pFormat;

    switch (nControlCode) {
        case 0:
            // Initialize camera device
            ec = CamDrv_Init(&CurrentContext);
            break;

        case 1:
            // Quit camera device
            CamDrv_Quit(&CurrentContext);
            CurrentContext.CaptureStatus = CamDrv_Status_Closed;
            break;

        case 2:
            // Get photo information and data
            // If continue snap is set, maybe there are more than 1 photo
#if (_TEST_TYPE == 41)
    uTimerStart = OSCR0;
#endif //_TEST_TYPE == 41
            if (pOutData->GetCapacity() < (int)sizeof(Camera_Photo)) {
                ec = E_OUT_OF_MEMORY;
                break;
            }
            if (CurrentContext.CaptureStatus == CamDrv_Status_Preview) {
                CamDrv_StopVideoCapture(&CurrentContext);
            }
//            ec = CamDrv_GetSnapFrames((Camera_Photo *)(char *)ebbOutData);
            CurrentContext.CaptureStatus = CamDrv_Status_Still;
            ec = CamDrv_StartStillCapture(&CurrentContext);
            if (FAILED(ec)) return ec;

            ec = CamDrv_GetStillPhoto(&CurrentContext, (Camera_Photo *)(char *)ebbOutData);
            pOutData->SetUsed(sizeof(Camera_Photo));
#if (_TEST_TYPE == 41)
    uTimerEnd = OSCR0;
    if (nCapturePhotoSampleCounter >= SAMPLETIMES) nCapturePhotoSampleCounter = 0;
    ulCapturePhotoTime[nCapturePhotoSampleCounter++] = \
                            (uTimerEnd >= uTimerStart) ? \
                            (uTimerEnd - uTimerStart) : \
                            ((ULONG)0xFFFFFFFF - uTimerStart + uTimerEnd);
#endif //_TEST_TYPE == 42
            break;

        case 3:
            // Begin preview
            if (ebbInData.GetCapacity() < (int)sizeof(RECT)) {
                ec = E_OUT_OF_MEMORY;
                break;
            }
            if (CurrentContext.CaptureStatus == CamDrv_Status_Preview) {
                break;
            }
            CurrentContext.CaptureStatus = CamDrv_Status_Preview;
            ec = CamDrv_StartVideoCapture(&CurrentContext);
            PoSetLcdPowerOn(TRUE);//tell power manager not to suspend lcd
            PoSetCameraPowerOn(TRUE);//tell power manager not to suspend camera
            break;

        case 4:
            // Stop preview
            if (CurrentContext.CaptureStatus == CamDrv_Status_Preview) {
                CamDrv_StopVideoCapture(&CurrentContext);
            }
            CurrentContext.CaptureStatus = CamDrv_Status_Unprepared;
            PoSetCameraPowerOn(FALSE);//tell power manager to suspend camera
            PoSetLcdPowerOn(FALSE);//tell power manager to suspend LCD
            break;

        case 5:
            // Release the memory of frames, except the default preview buffer.
            CamDrv_ReleaseFrames(&CurrentContext);
            break;

        case 6:
            // Set many formats for camera device, such as photo quality, brightness ...
            if (ebbInData.GetCapacity() < (int)sizeof(Camera_Format)) {
                ec = E_OUT_OF_MEMORY;
                break;
            }

            pFormat = (Camera_Format *)(char *)ebbInData;

            ec = CamDrv_SetAppFormat(&CurrentContext, pFormat->nID,
                    pFormat->wParam, pFormat->lParam);
            break;

        case 7:
            // Get many formats for camera device, such as photo quality, brightness ...
            if (ebbInData.GetCapacity() < (int)sizeof(int) ||
                pOutData->GetCapacity() < (int)sizeof(Camera_Format)) {
                ec = E_OUT_OF_MEMORY;
                break;
            }

            pFormat = (Camera_Format *)(char *)ebbOutData;
            pFormat->nID = *(int *)(char *)ebbInData;

            ec = CamDrv_GetAppFormat(&CurrentContext, pFormat->nID,
                    &pFormat->wParam, &pFormat->lParam);
            pOutData->SetUsed(sizeof(Camera_Format));
            break;

        case 8:
            // Enter photo/video mode.
            // Default thhe driver enters photo mode.
            if ((char *)ebbInData == NULL) CurrentContext.bVideo = FALSE;
            else CurrentContext.bVideo = *(int *)(char *)ebbInData;

            if (CurrentContext.bVideo) {
                CamDrv_SetVideoResolution(&CurrentContext,
                    CurrentContext.AppFormat.VideoResolution);
            }
            else {
                // Video buffer may be changed from video mode to still mode.
                // And video buffer is const (ALL QVGA) in still mode.
                if (CurrentContext.VideoBuffer.AllocBufSize > 0) {
                    DMAFreeBuffer(&CurrentContext, BUFTYPE_VIDEO);
                }
                CamDrv_SetPhotoResolution(&CurrentContext,
                    CurrentContext.AppFormat.PhotoResolution);
            }
            break;

        case 9: {
            // Begin video record
            if (!CurrentContext.bVideo) {
                ec = E_ACCESS_DENIED;
                break;
            }
            if (NULL == ppCompletionEvent) {
                ec = E_INVALID_ARGUMENT;
                break;
            }
            if (CurrentContext.CaptureStatus == CamDrv_Status_Preview) {
                CamDrv_StopVideoCapture(&CurrentContext);
            }

            CurrentContext.CaptureStatus = CamDrv_Status_Video;
            ec = CamDrv_StartVideoRecord(&CurrentContext, ppCompletionEvent);
            PoSetLcdPowerOn(TRUE);//tell power manager not to suspend lcd
            PoSetCameraPowerOn(TRUE);//tell power manager not to suspend camera
            break;
        }


        case 10:
            // Stop video record
            CurrentContext.CaptureStatus = CamDrv_Status_Unprepared;
            if (!CurrentContext.bVideo) {
                ec = E_ACCESS_DENIED;
                break;
            }
            CamDrv_StopVideoRecord(&CurrentContext);
            CurrentContext.CaptureStatus = CamDrv_Status_Unprepared;
            PoSetLcdPowerOn(FALSE);//tell power manager to suspend lcd
            PoSetCameraPowerOn(FALSE);//tell power manager to suspend camera
            break;

        case 11: {
            // Get image frame information and data,
            Camera_ImageFrame *pFrame;
            if (pOutData->GetCapacity() < (int)sizeof(Camera_ImageFrame)) {
                ec = E_OUT_OF_MEMORY;
                break;
            }
            pFrame = (Camera_ImageFrame *)(char *)ebbOutData;
            ec = CamDrv_GetVideoFrame(&CurrentContext, pFrame);

            pOutData->SetUsed(sizeof(Camera_ImageFrame));
            break;
        }

        // Power management code
        case 1000:
            // In preview, still, video mode, no need to close camera.
            if (CurrentContext.CaptureStatus == CamDrv_Status_Unprepared) {
                CamDrv_SaveFormat(&CurrentContext);
                CamDrv_CloseHardware(&CurrentContext);
                // Set the special status for power management.
                CurrentContext.CaptureStatus = CamDrv_Status_Paused;
            }
            break;

        case 1001:
        case 1002:
            if (CurrentContext.CaptureStatus == CamDrv_Status_Paused) {
                CamDrv_SetDefaultFormat(&CurrentContext);
                CamDrv_OpenHardware(&CurrentContext);
                CamDrv_ResumeFormat(&CurrentContext);
                CurrentContext.CaptureStatus = CamDrv_Status_Unprepared;
            }
            break;

#if (_TEST_TYPE == 41)
        case 4100:
            memcpy(pOutData->GetPayload(),
                    ul_DmaChYIsrTime,
                    SAMPLETIMES * sizeof(ULONG));
            pOutData->SetUsed(SAMPLETIMES * sizeof(ULONG));
            ec = NOERROR;
            break;
        case 4101:
            memcpy(pOutData->GetPayload(),
                    ul_DmaChCbIsrTime,
                    SAMPLETIMES * sizeof(ULONG));
            pOutData->SetUsed(SAMPLETIMES * sizeof(ULONG));
            ec = NOERROR;
            break;
        case 4102:
            memcpy(pOutData->GetPayload(),
                    ul_DmaChCrIsrTime,
                    SAMPLETIMES * sizeof(ULONG));
            pOutData->SetUsed(SAMPLETIMES * sizeof(ULONG));
            ec = NOERROR;
            break;

        case 4103:
            memcpy(pOutData->GetPayload(),
                    ulCapturePhotoTime,
                    SAMPLETIMES * sizeof(ULONG));
            pOutData->SetUsed(SAMPLETIMES * sizeof(ULONG));
            ec = NOERROR;
            break;
#endif // _TEST_TYPE 41

#ifdef PARAM_TEST
        case 10086:
            CurrentContext.BFW = *(int *)(char *)ebbInData;
            CurrentContext.BLW = *((int *)(char *)ebbInData + 1);
            break;
#endif


        default :
            ec = E_NOT_IMPLEMENTED;
            break;
    }

    return ec;
}

void CCamera::Dispose()
{
    delete this;
}

BOOL CCamera::Install()
{
    // Clear current camera context
    memset(&CurrentContext, 0, sizeof(CamDrv_Context));

    CurrentContext.CaptureStatus = CamDrv_Status_Closed;

    if (FAILED(CamDrv_Install(&CurrentContext))) return FALSE;
    return TRUE;
}

EXTERN IDeviceDriver * CDECL CreateCamera(uint_t uDeviceNo, void *pvParameter)
{
    CCamera *pCamera = new CCamera;

    if (!pCamera) goto ErrorExit;
    if (!pCamera->Install()) goto ErrorExit;

    kprintf("Add Camera OK!\n");

    return pCamera;

ErrorExit:
    kprintf("Add Camera Failed!\n");
    return NULL;
}
